package com.letoken.platform.app.consumer;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.letoken.platform.repo.performance.TeamPerformanceService;
import com.letoken.platform.pub.constant.ConstantsValue;
import com.letoken.platform.pub.enums.LetokenTokenTypeEnum;
import com.letoken.platform.pub.enums.TeamPerformanceTypeEnum;
import com.letoken.platform.pub.enums.TokenApprovedStateEnum;
import com.letoken.platform.pub.enums.TokenTransactionStatusEnum;
import com.letoken.platform.pub.util.RedisIdeTool;
import com.letoken.platform.repo.admin.dao.AdminIssueConfigMapper;
import com.letoken.platform.repo.admin.dao.WithdrawMapper;
import com.letoken.platform.repo.admin.po.AdminIssueConfig;
import com.letoken.platform.repo.admin.po.Withdraw;
import com.letoken.platform.repo.app.dao.*;
import com.letoken.platform.repo.app.po.AppUserAssetsHistory;
import com.letoken.platform.repo.app.po.AppUserTeam;
import com.letoken.platform.repo.app.po.AppUserTeamPath;
import com.letoken.platform.repo.app.po.AppUserWallet;
import com.letoken.platform.repo.utlis.lock.Lock;
import com.letoken.platform.repo.utlis.lock.LockCategoryEnum;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
@RabbitListener()
@RestController
public class WithdrawConsumer {
    private final static String exchange = "letoken.wallet_center";
    private final static String routingKey = "topic.#";
    private final static String queue = "topic.withdraw.response";
    @Bean
    public Queue withdrawQueue() {
        return new Queue("topic.#");
    }

    @Bean
    public CustomExchange withdrawExchange() {
        Map<String, Object> args = new HashMap<>(1);
        return new CustomExchange(exchange, "topic", true, false, args);
    }

    @Bean
    public Binding bindingWithdrawNotify(@Qualifier("withdrawQueue") Queue queue,
                                 @Qualifier("withdrawExchange") CustomExchange customExchange) {
        return BindingBuilder.bind(queue).to(customExchange).with(routingKey).noargs();
    }

    @Autowired
    AppUserRechargeMapper appUserRechargeMapper;
    @Autowired
    AppUserWalletMapper appUserWalletMapper;
    @Autowired
    AppUserAssetsHistoryMapper appUserAssetsHistoryMapper;
    @Autowired
    TokenMapper tokenMapper;
    @Autowired
    RedisIdeTool redisIdeTool;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    WithdrawMapper withdrawMapper;

    @Resource
    AppUserTeamPathMapper appUserTeamPathMapper;

    @Resource
    AppUserTeamMapper appUserTeamMapper;

    @Resource
    AdminIssueConfigMapper adminIssueConfigMapper;


    @Resource
    private TeamPerformanceService teamPerformanceService;



    @RabbitListener(queues = queue)
    @RabbitHandler
    public void withdrawConfirm(@Payload String message) {
        log.info("收到提现结果消息:{}",message);
        if (!StringUtils.hasLength(message)) return;
        WithdrawCallbackDto req = JSONObject.parseObject(message, WithdrawCallbackDto.class);
        try {
            if (!req.check() || !redisIdeTool.checkAndSetStr(RedisIdeTool.WITHDRAW_SEQ,req.getSeqNo())){
                // 拒绝消费 TODO 消息消费要改成手动消费
                throw new RuntimeException(String.format("参数错误 或 幂等校验未通过 %s",req));
            }

            LambdaQueryWrapper<Withdraw> lqwwd = new LambdaQueryWrapper<>();
            lqwwd.eq(Withdraw::getSn,req.getSeqNo())
                    .eq(Withdraw::getStatus,TokenApprovedStateEnum.PENDING.getValue());
            Withdraw withdraw = withdrawMapper.selectOne(lqwwd);
            if (ObjectUtils.isEmpty(withdraw)){
                log.info("【提现】记录流水号或者状态不匹配：{}",req);
                return;
            }
            Integer appUserWalletId = appUserWalletMapper.checkoutTokenIdAndUserId(withdraw.getTokenId(),withdraw.getUserId());
            if (ObjectUtils.isEmpty(appUserWalletId)){
                log.info("【提现】钱包记录不存在：{}",req);
                return;
            }
            if (!ObjectUtils.isEmpty(req.getOperatorId()) && req.getOperatorId() > ConstantsValue.I_0){
                withdraw.setApprovedBy(String.valueOf(req.getOperatorId()));
            }else {
                withdraw.setApprovedBy("系统自动");
            }


            req.setWithdraw(withdraw);
            req.setAppUserWalletId(appUserWalletId);
            WithdrawConsumer rc = SpringUtil.getBean(WithdrawConsumer.class);
            if (req.stateEnum == TokenApprovedStateEnum.ADOPT){
               rc.pass(req);
            }else if (req.stateEnum == TokenApprovedStateEnum.REFUSE){
               rc.refuse(req);
            }

            /*if(withdraw.getTransferMode().equals(1) && withdraw.getStatus().equals(1)){
                updateECo(withdraw.getUserId(),withdraw.getAmount());
            }*/
        } catch (Exception e) {
            // todo 如果出现异常，需要通知开发者
            log.error("提现-审核消息消费异常 {}", req,e);
            throw new RuntimeException(e);
        } finally {
            redisIdeTool.delStr(RedisIdeTool.WITHDRAW_SEQ,req.getSeqNo());
        }
    }





    @Transactional(rollbackFor = Exception.class)
    @Lock(ce = LockCategoryEnum.ID,value = "WALLET:MODIFY",field = "appUserWalletId",expire = 3l)
    public void pass(WithdrawCallbackDto req){
        Withdraw withdraw = req.getWithdraw();
        withdraw.setApprovedDate(LocalDateTime.now());
        withdraw.setStatus(1);
        withdraw.setTxid(req.txid);
        withdraw.setApprovedState(req.stateEnum.getValue());
        withdraw.setRemark(req.getRemark());

        LambdaQueryWrapper<AppUserAssetsHistory> lqwauah = new LambdaQueryWrapper<>();
        lqwauah.eq(AppUserAssetsHistory::getSn,withdraw.getSn())
                .eq(AppUserAssetsHistory::getUserId,withdraw.getUserId());
        AppUserAssetsHistory  appUserAssetsHistory = new AppUserAssetsHistory();
        appUserAssetsHistory.setStatus(TokenTransactionStatusEnum.COMPLETED.getValue());
        appUserAssetsHistory.setUpdateTime(LocalDateTime.now());
        appUserAssetsHistory.setTxid(req.txid);

        AppUserWallet appUserWallet = appUserWalletMapper.selectForUpdate(req.getAppUserWalletId());

        BigDecimal amount = withdraw.getAmount();
        if (amount.compareTo(appUserWallet.getFreezeBalance()) > 0) {
            log.info("【提现】提现金额超过冻结金额，解冻失败：{}",req);
            return;
        }

        AppUserWallet update = new AppUserWallet();
        update.setId(appUserWallet.getId());
        update.setFreezeBalance(appUserWallet.getFreezeBalance().subtract(withdraw.getAmount()));
        appUserAssetsHistory.setBalance(appUserWallet.getAvailableBalance());

        withdrawMapper.updateById(withdraw);
        appUserAssetsHistoryMapper.update(appUserAssetsHistory,lqwauah);
        appUserWalletMapper.updateById(update);

        if(LetokenTokenTypeEnum.CAP.getId().equals(appUserWallet.getTokenId())){
            teamPerformanceService.add(TeamPerformanceTypeEnum.CapWithdraw, withdraw.getUserId(), withdraw.getAmount());
        } else if(LetokenTokenTypeEnum.LET.getId().equals(appUserWallet.getTokenId())) {
            teamPerformanceService.add(TeamPerformanceTypeEnum.UsdtWithdraw, withdraw.getUserId(), withdraw.getAmount());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Lock(ce = LockCategoryEnum.ID,value = "WALLET:MODIFY",field = "appUserWalletId",expire = 3l)
    public void refuse(WithdrawCallbackDto req) {
        Withdraw withdraw = req.getWithdraw();
        withdraw.setStatus(3);
        withdraw.setApprovedState(req.getStateEnum().getValue());
        withdraw.setApprovedDate(LocalDateTime.now());

        LambdaQueryWrapper<AppUserAssetsHistory> lqwauah = new LambdaQueryWrapper<>();
        lqwauah.eq(AppUserAssetsHistory::getSn,withdraw.getSn())
                .eq(AppUserAssetsHistory::getUserId,withdraw.getUserId());
        AppUserAssetsHistory  appUserAssetsHistory = new AppUserAssetsHistory();
        appUserAssetsHistory.setStatus(TokenTransactionStatusEnum.FAIL.getValue());
        appUserAssetsHistory.setUpdateTime(LocalDateTime.now());


        AppUserWallet appUserWallet = appUserWalletMapper.selectForUpdate(req.getAppUserWalletId());

        BigDecimal amount = withdraw.getAmount();
        if (amount.compareTo(appUserWallet.getFreezeBalance()) > 0) {
            log.info("提现金额超过冻结金额，解冻失败....{}",req);
            return;
        }

        AppUserWallet update = new AppUserWallet();
        update.setId(appUserWallet.getId());
        update.setFreezeBalance(appUserWallet.getFreezeBalance().subtract(withdraw.getAmount()));
        update.setAvailableBalance(appUserWallet.getAvailableBalance().add(withdraw.getAmount()));
        appUserAssetsHistory.setBalance(update.getAvailableBalance());


        withdrawMapper.updateById(withdraw);
        appUserAssetsHistoryMapper.update(appUserAssetsHistory,lqwauah);
        appUserWalletMapper.updateById(update);
    }







    @Data
    public static class WithdrawCallbackDto implements Serializable{
        private String seqNo;
        private Integer status;
        private LocalDateTime createTime;
        private String remark;
        private Integer operatorId;
        private String txid;

        private Withdraw withdraw;
        private Integer appUserWalletId;

        private TokenApprovedStateEnum stateEnum;

        public Boolean check(){
            if (StringUtils.hasLength(seqNo)
                    && (status != null && status > 0)
                    && StringUtils.hasLength(seqNo)){
                TokenApprovedStateEnum stateEnum =  TokenApprovedStateEnum.getInstance(status);
                if (null != stateEnum){
                    this.stateEnum = stateEnum;
                    return true;
                }
            }
            log.info("参数错误:{}",this);
            return false;
        }

        @Override
        public String toString(){
            return JSON.toJSONString(this);
        }



    }


    public void updateECo(Integer userId,BigDecimal amount){
       AppUserTeamPath appUserTeamPath = appUserTeamPathMapper.selectById(userId);
       if(appUserTeamPath.getInvitePath()!=null && appUserTeamPath.getInvitePath()!=""){
           AdminIssueConfig adminIssueConfig = adminIssueConfigMapper.selectById(1);
           BigDecimal account = adminIssueConfig.getReduceEcoRate().multiply(amount);
           String[] path = appUserTeamPath.getInvitePath().split(",");
           for (String inviteID:path ) {
              AppUserTeam appUserTeam = appUserTeamMapper.selectById(inviteID);
              AppUserTeam team = new AppUserTeam();
               team.setUserId(Integer.valueOf(inviteID));
               team.setEcoValue(appUserTeam.getEcoValue().compareTo(account)>0?appUserTeam.getEcoValue().subtract(account):BigDecimal.ZERO);
               appUserTeamMapper.updateById(team);
           }
       }
    }

}
